#include "util.h"

///character operation 
void
delspace (char *c)
{
  char *i, *j;
  for (i = j = c; *i; i++)
    if (*i != ' ')
      *j++ = *i;
  *j = 0;
}

const char *
selectTo (char *word, const char *spec, char c)
{
  const char *f = strchr (spec, c);
  const char *e = strchr (spec, 0);
  if (f == NULL)
    f = e;
  strncpy (word, spec, f - spec);
  word[f - spec] = 0;
  delspace (word);
  return f;
}

const char *
selectProg (char *word, const char *spec)
{
  static char buf1[64];
  static char buf2[64];
  const char *c1 = selectTo (buf1, spec, '[');
  const char *c2 = selectTo (buf2, spec, '<');
  if (c1 < c2)
    {
      strcpy (word, buf1);
      return c1;
    }
  else
    {
      strcpy (word, buf2);
      return c2;
    }
}
const char *
next (const char *spec, char c)
{
  const char *c1 = strchr (spec, c);
  const char *c2 = strchr (spec, 0);
  return c1 ? c1 : c2;
}

const char *
next (const char *spec)
{
  const char *c1 = next (spec, '<');
  const char *c2 = next (spec, '[');
  return c1 < c2 ? c1 : c2;
}

///@ constructor 
ArgAnalyzer::ArgAnalyzer (const char *c, unsigned argc, const char **argv)
{
  _usage = c;
  construct (c);
  parse (argc, argv);

}
ArgAnalyzer::ArgAnalyzer (const char *c)
{
  _usage = c;
  construct (c);
}

///@ destructor
ArgAnalyzer::~ArgAnalyzer ()
{
  for (map < string, Option * >::iterator iter = _opts.begin ();
       iter != _opts.end (); ++iter)
    delete iter->second;
}

///@public function print
void
ArgAnalyzer::print ()
{
  cout << "Execution [ " << _program << " ]" << endl;
  cout << "[ Main Argument ";
  _main.print ();
  for (map < string, Option * >::iterator iter = _opts.begin ();
       iter != _opts.end (); ++iter)
    {
      cout << "\n[ " << iter->first;
      iter->second->print ();
    }
}
void
ArgAnalyzer::Option::print ()
{
  assert (arg.size () == type.size ());
  if (setted)
    assert (arg.size () == description.size ());

  if (setted)
    cout << " : SETTED ] " << endl;
  else
    cout << " : unset ] " << endl;

  if (setted)
    for (unsigned i = 0; i < type.size (); ++i)
      printf ("< %s : %s > = %s\n", description[i].c_str (), type[i].c_str (),
	      arg[i].c_str ());
  else
    for (unsigned i = 0; i < type.size (); ++i)
      printf ("< %s : %s > \n", description[i].c_str (), type[i].c_str ());
}

///private ArgAnalyzer function 
void
ArgAnalyzer::construct (const char *spec)
{
  static char word[1024];
  assert (strlen (spec) < 1024);

  spec = selectProg (word, spec);
  _program = word;
  while (*spec != 0)
    {
      if (*spec == '[')
	{
	  spec = selectTo (word, spec + 1, ']');
	  option (word);
	}
      else if (*spec == '<')
	{
	  spec = selectTo (word, spec + 1, '>');
	  optfield (&_main, word);
	}
      else
	assert (false);

      spec = next (spec + 1);
    }

}
int
ArgAnalyzer::parse (unsigned argc, const char **argv)
{
  // if( _program.compare( argv[0]) != 0 ) cout<<"ERROR program name "<<endl;
  if (argc == 1 && _main.type.size()!=0 )
    {
      usage ();
      if( MODE ==0 )exit (0);else return 1;
    }
  unsigned i = 1;
  assert (_main.type.size () == _main.description.size ());

  while (i < argc)
    {
      if (argv[i][0] == '-')
	{
	  if (_opts.find (argv[i]) == _opts.end ())
	    {
	      cout << "NOT EXIST the Argument : " << argv[i] << endl;
          if( MODE ==0 )exit (0);else return 1;
	    }
	  Option *opt = _opts[argv[i]];
	  opt->setted = true;
	  assert (opt->type.size () == opt->description.size ());
	  opt->arg.resize (opt->type.size ());
	  i++;

	  for (unsigned j = 0; j < opt->arg.size (); ++j)
	    {
	      if (i >= argc)
		{
		  cout << "ERROR OPTION ARGUMENT" << endl;
          if( MODE ==0 )exit (0);else return 1;
		}
	      if (argv[i][0] == '-')
		{
		  cout << "ERROR OPTION ARGUMENT" << endl;
          if( MODE ==0 )exit (0);else return 1;
		}
	      opt->arg[j] = argv[i];
	      i++;
	    }
	}
      else
	{
	  _main.arg.push_back (argv[i++]);
	}
    }
  if (_main.arg.size () != _main.type.size ())
    {
      cout << "ERROR MAIN ARGUMENT" << endl;
      if( MODE ==0 )exit (0);else return 1;
    }
  else
    _main.setted = true;

  return 0;
}
unsigned ArgAnalyzer::MODE = 0;
void
ArgAnalyzer::option (const char *spec)
{
  static char word[256];
  Option *opt = new Option ();
  spec = selectTo (word, spec, '<');
  _opts[word] = opt;
  while (*spec != 0)
    {
      if (*spec == '<')
	{
	  spec = selectTo (word, spec + 1, '>');
	  optfield (opt, word);
	}
      else
	assert (false);
      spec = next (spec + 1, '<');
    }
}
void
ArgAnalyzer::optfield (Option * opt, const char *spec)
{
  static char buf1[64];
  static char buf2[64];
  const char *c1 = selectTo (buf1, spec, ':');
  selectTo (buf2, c1 + 1, 0);
  opt->type.push_back (buf2);
  opt->description.push_back (buf1);
}
